package cn.tuling.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;

/**
 * Created on 2020/12/2.
 *
 * @author qiyang.
 * 描述：Aspect 切面理解
 */
@Aspect
@Component
public class LogUtil {

    /**
     * 第一个*标识忽略 访问修饰符及返回值类型
     * 两个 . 代表子包及子孙包
     * *代表所有类
     * *() 代表所有方法
     * (..) 两个 . 代表忽略所有参数
     */
    @Before("execution(* cn.tuling.user..*.*(..))")
    public static void before(JoinPoint joinPoint){
        System.out.println("前置通知");
        // 获取方法名
        String name = joinPoint.getSignature().getName();
        // 获取到参数
        Object[] args = joinPoint.getArgs();
    }

    @After("execution(* cn.tuling.user..*.*(..))")
    public static void after(){
        System.out.println("后置通知");
    }

    @AfterReturning(value = "execution(* cn.tuling.user..*.*(..))",
    returning = "returnValue")
    public static void afterReturning(Object returnValue){
        System.out.println("后置返回通知");
        System.out.println("返回值=>" + returnValue);
    }

    @AfterThrowing(value = "execution(* cn.tuling.user..*.*(..))", throwing = "ex")
    public static void afterException(Exception ex){
        System.out.println("后置异常通知");
        // 需要打印异常栈信息，以下三行代码是固定写法
        StringWriter sw = new StringWriter();
        ex.printStackTrace(new PrintWriter(sw, true));
        System.out.println("异常栈信息：" + sw.getBuffer().toString());
    }

    @Around("pointcount()")
    public static Object around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("环绕通知");
        // 获取方法名
        proceedingJoinPoint.getSignature().getName();
        // 获取参数
        proceedingJoinPoint.getArgs();
        Object obj = null;
        try {
            // 这里写前置通知逻辑
            System.out.println("这里写前置通知逻辑");
            proceedingJoinPoint.proceed();
            // 这里写后置通知逻辑
            System.out.println("这里写后置通知逻辑");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            // 这里写异常通知逻辑
            System.out.println("这里写异常通知逻辑");
        } finally {
            // 这里返回通知逻辑
            System.out.println("这里返回通知逻辑");
        }

        return obj;
    }

    /**
     * 统一定义切点表达式，其他通知直接写这个pointcount()方法即可，可复用性更好
     */
    @Pointcut("execution(* cn.tuling.user..*.*(..))")
    public void pointcount(){}

}
